#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <cerrno>
#include <functional>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "IntAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"

using namespace LogModule;

const static int gsockfd = -1;
//const static std::string gdefaultip = "127.0.0.1" //表示本地主机
const static uint16_t gdefaultport = 8080;

using func_t = std::function<std::string(const std::string&)>;

class UdpServer
{
public:
    //命令行输入ip + 端口号进行绑定, 虚拟机无需绑定ip, 只需指定端口号进行绑定即可
    UdpServer(func_t func, uint16_t port = gdefaultport)
        : _sockfd(gsockfd)
        , _addr(port)
        , _isrunning(false)
        , _func(func)
        {}
    //都是套路
    void InitServer()
    {
        //1.创建套接字
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); //指定网络通信模式. 面向数据包, 标记为设置为0
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket: " << strerror(errno);
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success, sockfd is" << _sockfd;
        
        //2.1填充网络信息并绑定, 网络信息都在sockaddr_in里面, 这里我们封装一下
        //2.2bind
        int n = ::bind(_sockfd, _addr.Netaddr(), _addr.NetAddrlen());
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind" << strerror(errno);
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";
    }

    //启动, cs模式,进行接受消息, 并且回显回去
    void Start()
    {
        _isrunning = true;
        while(true)
        {
            char inbuffer[1024];
            struct sockaddr_in peer; //客户端信息, 输出型参数
            socklen_t len = sizeof(peer);

            ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, CONV(&peer), &len);
            if(n > 0)
            {
                InetAddr cli(peer);
                inbuffer[n] = 0;

                //在这里进行业务处理
                std::string result = _func(inbuffer);

                //std::string clientinfo = cli.Ip() + ":" + std::to_string(cli.Port()) + '#' + inbuffer;
                //LOG(LogLevel::DEBUG) << clientinfo;
                //std::string echo_string = "echo#";
                //echo_string += inbuffer;
                //::sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, CONV(&peer), sizeof(peer));

                ::sendto(_sockfd, result.c_str(), result.size(), 0, CONV(&peer), sizeof(peer));
            }
        }
        _isrunning = false;
    } 
    ~UdpServer()
    {
        if(_sockfd > gsockfd)
            ::close(_sockfd);
    }
private:
    int _sockfd;
    InetAddr _addr;
    bool _isrunning;

    //业务处理
    func_t _func;
};